import { DialogFragment } from "~/utils/DialogFragment";
import mitt from "mitt";

/**
 * 弹窗管理，为单例；最好是不要直接使用
 * 做个广播
 */
export class DialogManager {
  private constructor() {}

  private static instance: DialogManager;

  /**
   * 广播接收者，各个 dialog 中通信使用的
   */
  readonly broadcastReceivers = mitt();

  static getInstance() {
    if (!this.instance) {
      this.instance = new DialogManager();
    }
    return this.instance;
  }

  private dialogCollection: Map<string, DialogFragment> = new Map();

  private showQueue: Map<string, DialogFragment>[] = [];

  private maxKey = 0;

  /**
   * 当前激活弹窗
   */
  private activeDialogFragment: DialogFragment | undefined;

  registerDialog(dialog: DialogFragment) {
    this.maxKey++;

    const key = this.maxKey.toString();
    this.dialogCollection.set(key, dialog);

    /**
     * 当弹窗打开的时候，就往队列里添加一个实例
     * 这里还需要考虑一个情况，就是在已显示的弹窗队列中，强制修改层级的问题
     */
    dialog.on("open", () => {
      this.showQueue.push(new Map().set(key, dialog));
      this.activeDialogFragment = dialog;
    });

    /**
     * 不是调用 close 方法关闭的
     */
    dialog.on("close", () => {
      const index = this.showQueue.findIndex((item) => item.get(key));
      if (index !== -1) {
        this.showQueue.splice(index, 1);
        const length = this.showQueue.length;
        if (length) {
          this.activeDialogFragment = this.showQueue[length - 1].get(key);
        }
      }
    });

    return {
      key: key,
      dialog: dialog as DialogFragment,
    };
  }

  show(key: string) {
    const queueDialog = this.showQueue.find((item) => item.get(key));

    /**
     * 如果在显示队列中就什么都不做
     * 其实是可以把它拿到最上层的,暂时定为什么都不做
     * 如果修改了层级，就需要修改的 activeDialogFragment
     */
    if (queueDialog) return queueDialog.get(key);

    const dialog = this.dialogCollection.get(key);

    if (dialog) {
      return dialog.show();
    } else {
      throw new Error("Unknown dialog");
    }
  }

  hide(key: string) {
    const index = this.showQueue.findIndex((item) => item.get(key));

    if (index !== -1) {
      return this.showQueue[index].get(key)?.hide();
    }
  }

  destroy(key: string) {
    this.hide(key);
    this.dialogCollection.get(key)?.destroy();
    this.dialogCollection.delete(key);
  }

  hideAll() {
    this.dialogCollection.forEach((dialog) => dialog.hide());
    this.showQueue = [];
    this.activeDialogFragment = undefined;
  }

  destroyAll() {
    this.dialogCollection.forEach((dialog) => dialog.destroy());
    this.dialogCollection.clear();
    this.showQueue = [];
    this.maxKey = 0;
    this.activeDialogFragment = undefined;
  }

  getActiveDialogFragment() {
    return this.activeDialogFragment;
  }

  getDialogCollection() {
    return this.dialogCollection;
  }
}
